package system

import (
	"context"
	"net/http"
	"path"
	"reflect"
	"strconv"
	"strings"
	"time"

	"portal/internal/common"
	"portal/internal/errorx"
	"portal/internal/svc"
	"portal/internal/tool/auth"
	"portal/internal/types"
	"portal/model"
	"portal/tools/encryption"
	"portal/tools/excel"

	"github.com/xuri/excelize/v2"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stringx"
)

type ImportUserLogic struct {
	logx.Logger
	ctx       context.Context
	svcCtx    *svc.ServiceContext
	userModel model.SysUserModel
	menuModel model.SysMenuModel
}

func NewImportUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ImportUserLogic {
	return &ImportUserLogic{
		Logger:    logx.WithContext(ctx),
		ctx:       ctx,
		svcCtx:    svcCtx,
		userModel: model.NewSysUserModel(svcCtx.Orm),
		menuModel: model.NewSysMenuModel(svcCtx.Orm),
	}
}

func (l *ImportUserLogic) ImportUser(r *http.Request) error {
	err := auth.HasPermission(l.menuModel, l.ctx, "system:user:import")
	if err != nil {
		return errorx.Unauthorized
	}
	authinfo, err := auth.GetAuth(l.ctx)
	if err != nil {
		return errorx.AuthNoFound
	}
	// todo: add your logic here and delete this line
	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		return nil
	}
	defer file.Close()
	fileType := path.Ext(fileHeader.Filename)
	if !stringx.Contains(common.AcceptExcelSuffix, fileType) {
		return errorx.FileTypeNoAdaptError
	}
	f, err := excelize.OpenReader(file)
	if err != nil {
		return errorx.FileParseError
	}
	rows, err := f.GetRows("sheet1")
	if err != nil {
		return errorx.FileParseError
	}
	um := excel.InitTag2FieldIdx(types.User{}, "xlsx_import")
	uservos := rowsToTableColumns(rows, um)
	userdtos := make([]*model.SysUser, 0)
	for _, user := range uservos {
		time := time.Now().Unix()
		userdto := &model.SysUser{
			UserName:    user.UserName,
			NickName:    user.NickName,
			UserType:    user.UserType,
			Avatar:      user.Avatar,
			Email:       user.Email,
			Phonenumber: user.Phonenumber,
			Sex:         user.Sex,
			Status:      user.Status,
			LoginDate:   time,
			UpdateTime:  time,
			CreateTime:  time,
			UpdateBy:    authinfo.NickName,
			CreateBy:    authinfo.NickName,
			DelFlag:     common.EXIST_FLAG,
		}
		if len(user.DeptId) > 0 {
			deptId, err := strconv.Atoi(user.DeptId)
			if err != nil {
				return errorx.ParamsError
			}
			userdto.DeptId.Int64 = int64(deptId)
			userdto.DeptId.Valid = true
		}
		pwd, err := encryption.EncryptionMd5(user.Password, l.svcCtx.Config.Salt)
		if err != nil {
			return errorx.NewBizError(500, err.Error())
		}
		userdto.Password = pwd
		userdtos = append(userdtos, userdto)
	}
	err = l.userModel.BulkInsert(l.ctx, nil, userdtos)
	return err
}

// 将 行数据 按照反射的方式转为结构体 TableColumn
func rowsToTableColumns(rows [][]string, tag2fieldIndex map[string]int) []*types.User {
	var data []*types.User
	// 默认第一行对应tag
	head := rows[0]
	for _, row := range rows[1:] {
		tbCol := &types.User{}
		rv := reflect.ValueOf(tbCol).Elem()
		for i := 0; i < len(row); i++ {
			colCell := row[i]
			// 通过 tag 取到结构体字段下标
			fieldIndex, ok := tag2fieldIndex[head[i]]
			if !ok {
				continue
			}
			colCell = strings.Trim(colCell, " ")
			// 通过字段下标找到字段放射对象
			v := rv.Field(fieldIndex)
			// 根据字段的类型，选择适合的赋值方法
			switch v.Kind() {
			case reflect.String:
				value := colCell
				v.SetString(value)
			case reflect.Int64, reflect.Int32:
				value, err := strconv.Atoi(colCell)
				if err != nil {
					panic(err)
				}
				v.SetInt(int64(value))
			case reflect.Float64:
				value, err := strconv.ParseFloat(colCell, 64)
				if err != nil {
					panic(err)
				}
				v.SetFloat(value)
			}
		}
		data = append(data, tbCol)
	}
	return data
}
